package com.mdroid.util;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Images.Media;

public class BitmapUtil {
    
    private static final int LARGE_SZIE = 400 * 400;
    private static final int SMALL_SIZE = 300 * 300;
    
    private static final int UNCONSTRAINED = -1;
    private static final long POLY64REV = 0x95AC9329AC4BC9B5L;
    private static final long INITIALCRC = 0xFFFFFFFFFFFFFFFFL;

    private static boolean init = false;
    private static long[] CRCTable = new long[256];

    /**
     * A function thats returns a 64-bit crc for string
     * 
     * @param in
     *            : input string
     * @return 64-bit crc value
     */
    public static final long Crc64Long(String in) {
        if (in == null || in.length() == 0) {
            return 0;
        }
        // http://bioinf.cs.ucl.ac.uk/downloads/crc64/crc64.c
        long crc = INITIALCRC, part;
        if (!init) {
            for (int i = 0; i < 256; i++) {
                part = i;
                for (int j = 0; j < 8; j++) {
                    int value = ((int) part & 1);
                    if (value != 0)
                        part = (part >> 1) ^ POLY64REV;
                    else
                        part >>= 1;
                }
                CRCTable[i] = part;
            }
            init = true;
        }
        int length = in.length();
        for (int k = 0; k < length; ++k) {
            char c = in.charAt(k);
            crc = CRCTable[(((int) crc) ^ c) & 0xff] ^ (crc >> 8);
        }
        return crc;
    }

    /**
     * A function that returns a human readable hex string of a Crx64
     * 
     * @param in
     *            : input string
     * @return hex string of the 64-bit CRC value
     */
    public static final String Crc64(String in) {
        if (in == null)
            return null;
        long crc = Crc64Long(in);
        /*
         * The output is done in two parts to avoid problems with
         * architecture-dependent word order
         */
        int low = ((int) crc) & 0xffffffff;
        int high = ((int) (crc >> 32)) & 0xffffffff;
        String outVal = Integer.toHexString(high) + Integer.toHexString(low);
        return outVal;
    }

    public static Bitmap getBitmap(String url) {
        Bitmap bitmap = null;
        try {
            bitmap = BitmapFactory.decodeStream(new URL(url).openStream());
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }
    
    /**
     * get Bitmap from the uri string
     * 
     * @author Abner.Wu
     * @date 2010-9-21
     * 
     */
    public static Bitmap getBitmapFromUri(String strUri) {
        URL imageUrl;
        Bitmap bitmap = null;
        InputStream is = null;
        try {
            // Create a Drawable by decoding a stream from a remote URL
            imageUrl = new URL(strUri);
            is = imageUrl.openStream();
            bitmap = BitmapFactory.decodeStream(is);
            is.close();
        } catch (Exception e) {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
        return bitmap;
    }
    
    /**
     * load Drawable image from drawableId
     * 
     * @param drawableId the id of the Drawable id
     * @param context Context
     * 
     * @author Abner.Wu
     * @date 2010-9-28
     */
    public static Drawable loadDrawable(int drawableId,Context context) {
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),drawableId);
        Drawable drawable = null;
        if (bitmap != null) {
            drawable = new BitmapDrawable(bitmap);
        }
        return drawable;
    }
    
    public static Bitmap getBitmapFromBytes(byte[] dataByte, boolean isLarge) {
        if(isLarge){
            return getBitmapFromBytes(dataByte, LARGE_SZIE);
        }else {
            return getBitmapFromBytes(dataByte, SMALL_SIZE);
        }
    }
    
    public static Bitmap getBitmapFromBytes(byte[] dataByte, int px) {
        Bitmap bmp = null;
        try {
            BitmapFactory.Options opts = new BitmapFactory.Options();
            opts.inJustDecodeBounds = true;
            BitmapFactory.decodeByteArray(dataByte, 0, dataByte.length, opts);

            if (px != 0) {
                opts.inSampleSize = computeSampleSize(opts, -1, px);
            }
            opts.inJustDecodeBounds = false;
            bmp = BitmapFactory.decodeByteArray(dataByte, 0, dataByte.length, opts);
        } catch (OutOfMemoryError err) {
            
        }
        return bmp;
    }
    
    /**
     * 
     * @param pathName
     * @return
     */
    public static Bitmap getBitmapFromPath(String pathName, boolean isLarge) {
        if(isLarge){
            return getBitmapFromPath(pathName, LARGE_SZIE);
        }else {
            return getBitmapFromPath(pathName, SMALL_SIZE);
        }
    }
    public static Bitmap getBitmapFromPath(String pathName, int px) {
        Bitmap bmp = null;
        try {
            BitmapFactory.Options opts = new BitmapFactory.Options();
            opts.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(pathName, opts);
            opts.inSampleSize = computeSampleSize(opts, -1, px);
            opts.inJustDecodeBounds = false;
            bmp = BitmapFactory.decodeFile(pathName, opts);
        } catch (OutOfMemoryError err) {
            
        }
        return bmp;
    }

    public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
        int initialSize = computeInitialSampleSize(options, minSideLength,maxNumOfPixels);

        int roundedSize;
        if (initialSize <= 8 ) {
            roundedSize = 1;
            while (roundedSize < initialSize) {
                roundedSize <<= 1;
            }
        } else {
            roundedSize = (initialSize + 7) / 8 * 8;
        }

        return roundedSize;
    }

    private static int computeInitialSampleSize(BitmapFactory.Options options,int minSideLength, int maxNumOfPixels) {
        double w = options.outWidth;
        double h = options.outHeight;

        int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
        int upperBound = (minSideLength == UNCONSTRAINED) ? 128 : (int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength));

        if (upperBound < lowerBound) {
            // return the larger one when there is no overlapping zone.
            return lowerBound;
        }

        if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
            return 1;
        } else if (minSideLength == -1) {
            return lowerBound;
        } else {
            return upperBound;
        }
    }

    public static Bitmap getBitmapFromInputStream(InputStream is, boolean isLarge) {
        if(isLarge){
            return getBitmapFromInputStream(is, LARGE_SZIE);
        }else {
            return getBitmapFromInputStream(is, SMALL_SIZE);
        }
    }

    private static Bitmap getBitmapFromInputStream(InputStream is, int px) {
        Bitmap bmp = null;
        try {
            BitmapFactory.Options opts = new BitmapFactory.Options();
//            opts.inJustDecodeBounds = true;
//            BitmapFactory.decodeStream(is, null, opts);
            opts.inSampleSize = computeSampleSize(opts, -1, px);
//            opts.inJustDecodeBounds = false;
            bmp = BitmapFactory.decodeStream(is, null, opts);
        } catch (OutOfMemoryError err) {
            
        }
        return bmp;
    }
    
    public static Bitmap getBitmapFromStream(InputStream is, boolean isLarge) {
        Bitmap bitmap = null;
        try {
            bitmap = BitmapUtil.getBitmapFromBytes(getBytes(is), isLarge);
        } catch (IOException e) {    
            e.printStackTrace();
        }
        
        return bitmap;
    }
    public static byte[] getBytes(InputStream is) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] b = new byte[1024];
        int len = 0;
        while ((len = is.read(b, 0, 1024)) != -1) {
            baos.write(b, 0, len);
            baos.flush();
        }
        byte[] bytes = baos.toByteArray();
        return bytes;
    }
    
    /**  
     * 以最省内存的方式读取本地资源的图片  
     * @param context  
     * @param resId  
     * @return  
     */  
    public static Bitmap getBitmap(Context context, int resId){   
        InputStream is = context.getResources().openRawResource(resId);
        return getBitmap(is);
    }
    
    public static Bitmap getBitmap(InputStream is){
        BitmapFactory.Options opt = new BitmapFactory.Options();
        opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
        opt.inPurgeable = true;
        opt.inInputShareable = true;
        
        return BitmapFactory.decodeStream(is,null,opt);
    }
    
    public static Bitmap drawableToBitmap(Drawable drawable) {
        Bitmap bitmap = Bitmap
                .createBitmap(
                        drawable.getIntrinsicWidth(),
                        drawable.getIntrinsicHeight(),
                        drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
                                : Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable
                .getIntrinsicHeight());
        drawable.draw(canvas);
        return bitmap;
    }
    
    public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, float roundPx) {
        try {
            int width = bitmap.getWidth(), height = bitmap.getHeight();
            Bitmap output = Bitmap
                    .createBitmap(width, height, Config.ARGB_8888);
            Canvas canvas = new Canvas(output);

            int color = 0xff424242;
            Paint paint = new Paint();
            Rect rect = new Rect(0, 0, width, height);
            RectF rectF = new RectF(rect);

            paint.setAntiAlias(true);
            canvas.drawARGB(0, 0, 0, 0);
            paint.setColor(color);
            canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

            paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
            canvas.drawBitmap(bitmap, rect, rect, paint);
            canvas = null;
            paint = null;
            rect = null;
            rectF = null;
            if (!bitmap.isRecycled()) {
                bitmap.recycle();
            }

            return output;

        } catch (Exception e) {
            e.printStackTrace();
            return bitmap;
        }
    }
    
    public static Bitmap transform(Matrix scaler, Bitmap source, int targetWidth, int targetHeight, boolean scaleUp) {
        int deltaX = source.getWidth() - targetWidth;
        int deltaY = source.getHeight() - targetHeight;
        if (!scaleUp && (deltaX < 0 || deltaY < 0)) {
            /*
             * In this case the bitmap is smaller, at least in one dimension,
             * than the target. Transform it by placing as much of the image as
             * possible into the target and leaving the top/bottom or left/right
             * (or both) black.
             */
            Bitmap b2 = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888);
            Canvas c = new Canvas(b2);

            int deltaXHalf = Math.max(0, deltaX / 2);
            int deltaYHalf = Math.max(0, deltaY / 2);
            Rect src = new Rect(deltaXHalf, deltaYHalf, deltaXHalf + Math.min(targetWidth, source.getWidth()),
                    deltaYHalf + Math.min(targetHeight, source.getHeight()));
            int dstX = (targetWidth - src.width()) / 2;
            int dstY = (targetHeight - src.height()) / 2;
            Rect dst = new Rect(dstX, dstY, targetWidth - dstX, targetHeight - dstY);
            c.drawBitmap(source, src, dst, null);
            return b2;
        }
        float bitmapWidthF = source.getWidth();
        float bitmapHeightF = source.getHeight();

        float bitmapAspect = bitmapWidthF / bitmapHeightF;
        float viewAspect = (float) targetWidth / targetHeight;

        if (bitmapAspect > viewAspect) {
            float scale = targetHeight / bitmapHeightF;
            if (scale < .9F || scale > 1F) {
                scaler.setScale(scale, scale);
            } else {
                scaler = null;
            }
        } else {
            float scale = targetWidth / bitmapWidthF;
            if (scale < .9F || scale > 1F) {
                scaler.setScale(scale, scale);
            } else {
                scaler = null;
            }
        }

        Bitmap b1 = null;
        if (scaler != null) {
            try {
                // this is used for minithumb and crop, so we want to filter
                // here.
                b1 = Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), scaler, true);
            } catch (OutOfMemoryError e) {
                b1 = Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), scaler, true);
            }
        } else {
            b1 = source;
        }

        int dx1 = Math.max(0, b1.getWidth() - targetWidth);
        int dy1 = Math.max(0, b1.getHeight() - targetHeight);

        Bitmap b2 = Bitmap.createBitmap(b1, dx1 / 2, dy1 / 2, targetWidth, targetHeight);

        if (b1 != source) {
            b1.recycle();
        }

        return b2;
    }
    
    /**
     * 旋转bitmap到指定的角度，如果新bitmap创建成功，原图将被回收
     * @param b
     * @param degrees
     * @return
     */
    public static Bitmap rotate(Bitmap b, int degrees) {
        return rotateAndMirror(b, degrees, false);
    }

    /**
     * 创建镜像并旋转bitmap到指定的角度，如果新bitmap创建成功，原图将被回收
     * @param b
     * @param degrees
     * @param mirror
     * @return
     */
    public static Bitmap rotateAndMirror(Bitmap b, int degrees, boolean mirror) {
        if ((degrees != 0 || mirror) && b != null) {
            Matrix m = new Matrix();
            m.setRotate(degrees, (float) b.getWidth() / 2, (float) b.getHeight() / 2);
            if (mirror) {
                m.postScale(-1, 1);
                degrees = (degrees + 360) % 360;
                if (degrees == 0 || degrees == 180) {
                    m.postTranslate((float) b.getWidth(), 0);
                } else if (degrees == 90 || degrees == 270) {
                    m.postTranslate((float) b.getHeight(), 0);
                } else {
                    throw new IllegalArgumentException("Invalid degrees=" + degrees);
                }
            }

            try {
                Bitmap b2 = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, true);
                if (b != b2) {
                    b.recycle();
                    b = b2;
                }
            } catch (OutOfMemoryError ex) {
                // We have no memory to rotate. Return the original bitmap.
            }
        }
        return b;
    }
    
    /**
     * 将图片插入本地相册数据库，并且生成缩略图
     * @author William.cheng
     * @version 创建时间：2011-12-19 上午9:58:27
     * @param cr
     * @param source 图片路径
     * @param title 图片标题
     * @param description 图片描述
     * @return
     */
     public static final String insertImage(ContentResolver cr, String source,
             String title, String description) {
         ContentValues values = new ContentValues();
         values.put(Images.Media.TITLE, title);
         values.put(Images.Media.DATA, source);
         values.put(Images.Media.DESCRIPTION, description);
         values.put(Images.Media.MIME_TYPE, "image/jpeg");

         Uri url = null;
         String stringUrl = null; /* value to be returned */

         try {
             url = cr.insert(Media.EXTERNAL_CONTENT_URI, values);

             if (source != null) {

                 long id = ContentUris.parseId(url);
                 // Wait until MINI_KIND thumbnail is generated.
                 Bitmap miniThumb = Images.Thumbnails.getThumbnail(cr, id,
                         Images.Thumbnails.MINI_KIND, null);
                 // This is for backward compatibility.
                 Bitmap microThumb = StoreThumbnail(cr, miniThumb, id, 50F, 50F,
                         Images.Thumbnails.MICRO_KIND);
                 miniThumb.recycle();
                 microThumb.recycle();
             } else {
                 cr.delete(url, null, null);
                 url = null;
             }
         } catch (Exception e) {
             if (url != null) {
                 cr.delete(url, null, null);
                 url = null;
             }
         }

         if (url != null) {
             stringUrl = url.toString();
         }

         return stringUrl;
     }

     private static final Bitmap StoreThumbnail(ContentResolver cr,
             Bitmap source, long id, float width, float height, int kind) {
         // create the matrix to scale it
         Matrix matrix = new Matrix();

         float scaleX = width / source.getWidth();
         float scaleY = height / source.getHeight();

         matrix.setScale(scaleX, scaleY);

         Bitmap thumb = Bitmap.createBitmap(source, 0, 0, source.getWidth(),
                 source.getHeight(), matrix, true);

         ContentValues values = new ContentValues(4);
         values.put(Images.Thumbnails.KIND, kind);
         values.put(Images.Thumbnails.IMAGE_ID, (int) id);
         values.put(Images.Thumbnails.HEIGHT, thumb.getHeight());
         values.put(Images.Thumbnails.WIDTH, thumb.getWidth());

         Uri url = cr.insert(Images.Thumbnails.EXTERNAL_CONTENT_URI, values);

         try {
             OutputStream thumbOut = cr.openOutputStream(url);

             thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut);
             thumbOut.close();
             return thumb;
         } catch (FileNotFoundException ex) {
             return null;
         } catch (IOException ex) {
             return null;
         }
     }
}
